package com.bblocks.auth.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bblocks.auth.pojo.ResultLogin;
import com.bblocks.auth.pojo.entity.Users;
import com.bblocks.auth.service.SysAppGroupUserService;
import com.bblocks.auth.service.UsersService;
import com.bblocks.auth.utils.SelfHttpConUtil;
import com.bblocks.common.BaseController;
import com.bblocks.enums.AppTypeEnum;
import com.bblocks.exception.ServiceException;
import com.bblocks.common.Constant;
import com.bblocks.enums.SysEnum;

import com.bblocks.util.MobileUtil;
import com.bblocks.util.PropertiesUtil;
import com.bblocks.util.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.security.spec.AlgorithmParameterSpec;
import java.util.*;


/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @author Mr.Chen
 * @since 2019-07-27
 */

@Api(value = "用户注册登录相关", tags = {"用户注册登录相关"})
@RestController
@RequestMapping("/users")
@PropertySource({"classpath:/app.properties"})
@Slf4j
public class UsersController extends BaseController {

	@Autowired
	private UsersService usersService;

	@Value("${LOGIN_ERROR_HOUR}")
	private Integer loginErrorHour;

	@Value("${default.verification.code}")
	private String defaultVerificationCode;
	@Autowired
	private SysAppGroupUserService sysAppGroupUserService;

	/*@Autowired
	UserFeign userFeign;*/

	@ApiImplicitParams({
			@ApiImplicitParam(name = "userPhone", value = "手机号/登录名，优先认为是手机号前提是手机号格式", required = true, dataType = "String", paramType = "query"),
			@ApiImplicitParam(name = "userPassword", value = "密码", required = true, dataType = "String", paramType = "query"),
			@ApiImplicitParam(name = "type", value = "角色：0=app,1=商家,2=合伙人（默认app）", required = false, dataType = "int", paramType = "query"),

	})
	@ApiOperation(value = "手机号/登录名密码登录", notes = "目前用户信息给的还是太多了(无userPhone则提示绑定手机号)")
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public ResultLogin<Users> login(String userPhone
			, String userPassword
			, Integer type) {
		Assert.notNull(userPhone, "userPhone不能为空！");
		Assert.notNull(userPassword, "userPassword不能为空！");
		//log.info("UsersController.login============:"+userPhone);
		AppTypeEnum appType = AppTypeEnum.getAppTypeEnum(type);
		if (null == type) {
			appType = AppTypeEnum.App;
			//userType = 0;//0  非商家端(app)    1商家端登录()  2 xxx
		}
		boolean isMobile = MobileUtil.checkPhone(userPhone);
		Users user = null;
		if (isMobile)
			user = this.usersService.getByUserPhone(userPhone);
		if (user == null) {
			user = this.usersService.getByName(userPhone);
		}

		if (BeanUtil.isEmpty(user)) {
			return ResultLogin.SysError(SysEnum.NOT_USER);
		}
		if ("0".equals(user.getUserStatus())) {
			return ResultLogin.SysError(SysEnum.USER_FORBIDDEN);
		}

		//登入之前是否重试次数达到三次以上且在约束的时间范围内 校验==begin
		Date lastLogin = new Date();
		if (ObjectUtil.isNotNull(user.getUserCurrentLogintime())) {
			lastLogin = user.getUserCurrentLogintime();
		}
		if (user.getLoginErrorNum() != null && user.getLoginErrorNum() >= 300 && DateUtil.between(lastLogin, DateUtil.date(), DateUnit.HOUR) < loginErrorHour) {
			return ResultLogin.failed(0, "密码连续错误达三次请在" + loginErrorHour + "小时后再重试!");
		}

		log.debug("login cost begin");
		long b = System.currentTimeMillis();
		this.usersService.login(user, appType,this.getAppPlatformType(), userPassword);
		log.debug("login cost:{}", (System.currentTimeMillis() - b));
		return ResultLogin.okData(user);
	}

	@ApiImplicitParams({
			@ApiImplicitParam(name = "userPhone", value = "手机号", required = true, dataType = "String", paramType = "query"),
			@ApiImplicitParam(name = "verificationCode", value = "验证码", required = true, dataType = "String", paramType = "query"),
			@ApiImplicitParam(name = "verificationCodeType", value = "验证码类型", required = true, dataType = "String", paramType = "query"),
			@ApiImplicitParam(name = "type", value = "角色：0=app,1=商家,2=合伙人（默认app）", required = false, dataType = "int", paramType = "query"),

	})
	@ApiOperation(value = "验证码登录", notes = "目前用户信息给的还是太多了(无userPhone则提示绑定手机号1)")
	@RequestMapping(value = "/loginVerificationCode", method = RequestMethod.POST)
	public ResultLogin<Users> loginVerificationCode(@RequestParam(value = "userPhone", required = true) String userPhone
			, @RequestParam(value = "verificationCode", required = true) String verificationCode
			, @RequestParam(value = "verificationCodeType", required = true) String verificationCodeType
			, @RequestParam(value = "type", required = false) Integer type) {

		// 登录之前校验验证码
//		R sysEnumR = userFeign.checkVerifi(userPhone, verificationCode, verificationCodeType);
		R sysEnumR = R.ok();
		//if (sysEnumR.getResult() != 1 && sysEnumR.getCode() != 1) {
			// 验证码校验失败, 判断是不是灰度用户,(过程: 查询生效的灰度组, 根据生效的灰度组合手机号取灰度用户, 校验灰度用户的默认验证码, 如果没有默认的验证码,直接通过)
			// 1. 查询灰度组是否存在, 生效的灰度组
			/*List<SysAppGroup> list = sysAppGroupService.list(Wrappers.<SysAppGroup>lambdaQuery()
					.eq(SysAppGroup::getGroupStatus, 1));
			if (CollectionUtils.isEmpty(list)) {
				log.warn("手机号:{}, 验证码登录验证失败, 并且灰度组不存在", userPhone);
				return ResultLogin.SysError(sysEnumR.getMessage());
			}
			// 2.判断灰度用户是否存在
			long[] groupIds = list.stream().mapToLong(SysAppGroup::getId).toArray();
			List<SysAppGroupUser> sysAppGroupUserList = sysAppGroupUserService.list(Wrappers.<SysAppGroupUser>lambdaQuery()
					.eq(SysAppGroupUser::getMobile, userPhone).in(SysAppGroupUser::getGroupId, Longs.asList(groupIds)));
			if (CollectionUtils.isEmpty(sysAppGroupUserList)) {
				log.warn("手机号:{}, 验证码登录验证失败, 并且不是灰度用户", userPhone);
				return ResultLogin.SysError(sysEnumR.getMessage());
			}
			// 如果有多个,就取第一个灰度用户
			SysAppGroupUser appGroupUser = sysAppGroupUserList.get(0);
			// 判断检验灰度用户是否有默认的验证码, 如果有, 就校验
			if (Objects.nonNull(appGroupUser.getChkCode())) {
				// 对比验证码是否相同, 灰度初始化验证码和输入验证码是否相同
				if (!Objects.equals(verificationCode, appGroupUser.getChkCode())) {
					log.warn("手机号:{}, 验证码登录验证失败, 默认的灰度验证码输入错误; 验证码:{}, 灰度默认验证码:{}", userPhone, verificationCode, appGroupUser.getChkCode());
					return ResultLogin.SysError(sysEnumR.getMessage());
				}
			} else {
				// 如果没有默认用户, 就取nacos中默认配置的验证码
				if (!Objects.equals(verificationCode, defaultVerificationCode)) {
					log.warn("手机号:{}, 验证码登录验证失败, 默认的灰度验证码输入错误; 验证码:{}, 灰度默认验证码:{}", userPhone, verificationCode, defaultVerificationCode);
					return ResultLogin.SysError(sysEnumR.getMessage());
				}
			}*/

//		}

		//log.info("UsersController.login============:"+userPhone);
		AppTypeEnum appType = AppTypeEnum.getAppTypeEnum(type);
		if (null == type) {
			appType = AppTypeEnum.App;
			//userType = 0;//0  非商家端(app)    1商家端登录()  2 xxx
		}
		Users user = this.usersService.getByUserPhone(userPhone);
		if (BeanUtil.isEmpty(user)) {
			return ResultLogin.SysError(SysEnum.NOT_USER);
		}
		if ("0".equals(user.getUserStatus())) {
			return ResultLogin.SysError(SysEnum.USER_FORBIDDEN);
		}

		this.usersService.login(user, appType, this.getAppPlatformType(),null);

		return ResultLogin.okData(user);
	}

	// 退出登录(不管咋樣都退出，且提示成功)
	@RequestMapping(value = "/exitLogin", method = RequestMethod.POST)
	public ResultLogin exitLogin(HttpServletRequest request) {
		//String token = request.getHeader(PropertiesUtil.getValue("access_token"));
		StpUtil.logout();

		return ResultLogin.ok();
	}


	/**
	 * 登录(如无提示新注册or绑定)
	 *
	 * @Description: 根据微信小程序的账号、密码。 1. 获取openid/session-key==>一个授权的过程  2.判断应用程序服务器是否注册过（判断下一步是否注册还是进入首页）
	 * @Param:
	 * @Return:
	 * @Author: Mr.Chen
	 * @Date: 2019/7/18 15:09
	 */
	@PostMapping("/userAuthorizeForWx")
	public ResultLogin<Users> userLoginAuthorizeForWx(String code, Integer type) {
		AppTypeEnum appType = AppTypeEnum.getAppTypeEnum(type);
		if (null == type) {
			appType = AppTypeEnum.App;
			//userType = 0;//0  非商家端(app)    1商家端登录()  2 xxx
		}
		SysEnum commonReturnCode = SysEnum.SUCCESS;
		log.info("UsersController.userLoginAuthorizeForWx============:" + code);

		//1.获取sessionKey/openid
		String appid = PropertiesUtil.getValue("appid");
		String appsecret = PropertiesUtil.getValue("appsecret");
		if (null != type && type == 1) {
			appid = PropertiesUtil.getValue("sbAppid");
			appsecret = PropertiesUtil.getValue("sbAppsecret");
		}
		String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + appsecret + "&js_code=" + code + "&grant_type=authorization_code";   //接口地址
		String jsonStr = SelfHttpConUtil.sendGetReq(url);// 发送http请求
		JSONObject jsonObject = JSONObject.parseObject(jsonStr);
		String session_key = jsonObject.getString("session_key");
		String openid = jsonObject.getString("openid");
		if (StrUtil.isEmpty(openid)) {
			log.warn("提取openId返回异常：{}", jsonStr);
			throw new ServiceException(SysEnum.SYS_ERROR);
		}
		//2.校验用户是否注册过
		Users users = new Users();
		if (null != type && type == 1) {
			users.setSbOpenId(openid);
		} else {
			type = 0;
			users.setWxOpenId(openid);
		}
		users = this.usersService.getOne(new LambdaQueryWrapper<Users>(users));
		if (null == users) {//此时未注册过...提示下一步开始注册登入
			users = new Users();
			if (null != type && type == 1) {
				users.setSbOpenId(openid);
				users.setSbSessionKey(session_key);
			} else {
				users.setWxOpenId(openid);
				users.setWxSessionKey(session_key);
			}
			commonReturnCode = SysEnum.NOT_USER;
			return new ResultLogin(commonReturnCode.getCode(), commonReturnCode.getMessage(), users);
		} else {//此时注册过..直接登入即可
			//更新登入时间...且将最新的sessionkey存入数据库(备用)
			if (null != type && type == 1) {
				users.setSbSessionKey(session_key);
			} else {
				users.setWxSessionKey(session_key);
			}


			this.usersService.login(users, appType,this.getAppPlatformType(), null);
			commonReturnCode = SysEnum.SUCCESS;
			return ResultLogin.okData(users);
		}
	}


	/**
	 * 注册or绑定老账户并登录
	 *
	 * @Description: 微信小程序的登入到平台接口
	 * @Param: openId/sessionKey/userPhone...
	 * @Return:
	 * @Author: Mr.Chen
	 * @Date: 2019/7/18 16:31
	 */
	/*@PostMapping("/userLoginForWx")
	public ResultLogin<Users> userLoginForWx(Users param, HttpServletRequest request) {
		log.info("UsersController.userLoginForWx===========" + param.toString());

		if (param.getUserPhone() == null || StrUtil.isEmpty(param.getWxOpenId() + param.getSbOpenId())) {
			return ResultLogin.SysError(SysEnum.PARAMS_IS_NULL);
		}

		AppTypeEnum appType = AppTypeEnum.getAppTypeEnum(param.getType());
		if (null == appType) {
			appType = AppTypeEnum.App;
		}
		if (param.getType() == null) ;

		//1.判断当前的手机号是否在平台中注册过
		Users user = this.usersService.getByUserPhone(param.getUserPhone());

		Integer userType = null;
		String openId = null;
		if (StrUtil.isNotEmpty(param.getWxOpenId())) {
			userType = 0;
			openId = param.getWxOpenId();
		} else if (StrUtil.isNotEmpty(param.getSbOpenId())) {
			userType = 1;
			openId = param.getSbOpenId();
		}

		if (user == null) {
			UserMobileForm form = new UserMobileForm();
			form.setMobile(param.getUserPhone());
			if (StrUtil.isNotEmpty(param.getWxOpenId())) {
				form.setOpenId(param.getWxOpenId());
				form.setRole(0);
			} else if (StrUtil.isNotEmpty(param.getSbOpenId())) {
				form.setOpenId(param.getSbOpenId());
				form.setRole(1);
			}
			form.setNickName(param.getNickName());
			form.setHeadPicture(param.getHeadPicture());
			form.setGender(param.getGender());
			form.setIp(ServletUtil.getClientIP(request));
			Result r = userFeign.registerInner(form);
			if (r.getResult() != CommonConstants.SUCCESS) {
				log.error("注册：{} 失败：{}（{}）", param.getUserPhone(), r.getMessage(), r.getResult());
				return ResultLogin.failed(SysEnum.ERROR.getCode(), "系统故障,注册失败,请稍后再试！");
			}

			user = this.usersService.getByUserPhone(param.getUserPhone());
		} else {//让你过来绑定的，需要确认密码什么的是吧(暂时不管，后期通过微信校验手机号是否ok）

			usersService.updateUserOpenid(user.getId(), userType, openId);
		}

		this.usersService.login(user, appType, AppPlatformTypeEnum.Wx,null);
		return ResultLogin.okData(user);
	}*/


	/**
	 * @Description: 根据参数获取用户在腾讯中绑定的手机号
	 * @Param: encrypdata/ivdata/sessionkey
	 * @Return:
	 * @Author: Mr.Chen
	 * @Date: 2019/7/19 11:20
	 */
	@PostMapping("/getUserPhoneInfoForWxByParams")
	public ResultLogin<String> getUserPhoneInfoForWx(String encrypdata, String ivdata, String sessionKey) {
		byte[] encrypDataArr = Base64.decode(encrypdata.getBytes());
		byte[] ivDataArr = Base64.decode(ivdata.getBytes());
		byte[] sessionKeyArr = Base64.decode(sessionKey.getBytes());
		String str = "";
		try {
			str = decryptForWx(sessionKeyArr, ivDataArr, encrypDataArr);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return ResultLogin.okData(str);
	}


	/**
	 * @Description: 根据参数获取用户在腾讯中绑定的手机号==》辅助...
	 * @Param: encrypdata/ivdata/sessionkey
	 * @Return:
	 * @Author: Mr.Chen
	 * @Date: 2019/7/19 11:20
	 */
	private String decryptForWx(byte[] sessionKeyArr, byte[] ivDataArr, byte[] encrypDataArr) {
		try {
			AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivDataArr);
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			SecretKeySpec keySpec = new SecretKeySpec(sessionKeyArr, "AES");
			cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
			//解析解密后的字符串  
			return new String(cipher.doFinal(encrypDataArr), "UTF-8");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	@GetMapping("/getPublicKey")
	public R<?> getPublicKey() {
		return R.ok(PropertiesUtil.getValue("public_key"));
	}

	//-----------------------------------------------------------------------------------------------------------------------------


	/**
	 * app微信登录(暂时屏蔽)
	 *
	 * @return
	 * @GetMapping("/userAppAuthorizeForWx") public R<Users> userAppAuthorizeForWx(String wxAppOpenId) {
	 * R<Users> response = usersService.userAppAuthorizeForWx(wxAppOpenId);
	 * return response;
	 * }
	 */

	//app微信判断手机是否已绑定
	@GetMapping("/isBindUserPhone")
	public ResultLogin isBindUserPhone(String userPhone) {
		boolean flag = usersService.isBindUserPhone(userPhone);
		if (flag) {
			return ResultLogin.SysError(SysEnum.BIND_ERROR);
		}
		return ResultLogin.ok();

	}

	@ApiOperation(value = "用户登录信息（只有最基本的信息）", notes = "")
	@GetMapping("/info")
	public R<ComUser> info() {
		if (StpUtil.isLogin())
			return R.ok((ComUser) StpUtil.getSession().get(Constant.USER));
		else
			return R.sysError(SysEnum.NOT_LOGIN);
	}

}

